﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using WindowsPhoneMVC.Navigation;
using WindowsPhoneMVC.Tests.Navigation.Fakes;
using WindowsPhoneMVC.Tests.Phone.Fakes;

namespace WindowsPhoneMVC.Tests.Navigation
{
    [TestClass]
    public class ControllerActionsTests
    {
        private FakeMvcFrame fakeMvcFrame;
        private FakeJournal fakeJournal;
        private FakeNavigator navigator;
        public List<string> EmptyList;
        private const string FrameName = "TestFrame";

        [TestInitialize]
        public void Setup()
        {
            fakeMvcFrame = new FakeMvcFrame();
            fakeJournal = new FakeJournal();
            navigator = new FakeNavigator();
            navigator.Initialise(fakeJournal, fakeMvcFrame, FrameName);
        }

        [TestMethod]
        public void navigate_to_action()
        {
            // arrange
            var viewModel = new ViewModelBase { Navigator = navigator };
            var requestBuilder = new ControllerActions<FakeController>(viewModel);

            // act
            requestBuilder.Invoke(c => c.SomeAction());

            // assert
            var navigationRequest = fakeJournal.PopEntry();
            Assert.IsTrue(fakeJournal.IsEmpty());
            Assert.AreEqual("SomeAction", navigationRequest.ActionName);
            Assert.AreEqual("TestController", navigationRequest.ControllerName);
            Assert.AreEqual(navigator.NavigationFrameName, navigationRequest.NavigationFrameName);
        }

        [TestMethod]
        public void navigate_to_action_with_single_argument()
        {
            // arrange
            var viewModel = new ViewModelBase { Navigator = navigator };
            var requestBuilder = new ControllerActions<FakeController>(viewModel);

            // act
            requestBuilder.Invoke(c => c.SomeAction(1));

            // assert
            var navigationRequest = fakeJournal.PopEntry();
            Assert.IsTrue(fakeJournal.IsEmpty());
            Assert.AreEqual("SomeAction", navigationRequest.ActionName);
            Assert.AreEqual("TestController", navigationRequest.ControllerName);
            Assert.AreEqual(FrameName, navigationRequest.NavigationFrameName);
            //TODO I recon it should be pretty easy to get this to pass
            //Assert.Equal("arg", navigationRequest.Parameters.Single().Name);
            Assert.AreEqual(1, navigationRequest.Parameters.Single().Value);
        }

        [TestMethod]
        public void navigate_to_action_with_two_arguments()
        {
            // arrange
            var viewModel = new ViewModelBase { Navigator = navigator };
            var requestBuilder = new ControllerActions<FakeController>(viewModel);
            EmptyList = new List<string>();

            // act
            requestBuilder.Invoke(c => c.SomeAction(1, EmptyList));

            // assert
            var navigationRequest = fakeJournal.PopEntry();
            Assert.IsTrue(fakeJournal.IsEmpty());
            Assert.AreEqual("SomeAction", navigationRequest.ActionName);
            Assert.AreEqual("TestController", navigationRequest.ControllerName);
            Assert.AreEqual(navigator.NavigationFrameName, navigationRequest.NavigationFrameName);
            //TODO I recon it should be pretty easy to get this to pass
            //Assert.Equal("arg", navigationRequest.Parameters.Single().Name);
            Assert.AreEqual(1, navigationRequest.Parameters.First().Value);
            Assert.AreEqual(EmptyList, navigationRequest.Parameters.Last().Value);
        }

        [TestMethod]
        public void navigate_to_using_complete_expression_with_no_args()
        {
            // arrange
            var viewModel = new ViewModelBase { Navigator = navigator };
            var requestBuilder = new ControllerActions<FakeController>(viewModel);

            // act
            requestBuilder.Invoke(c => c.SomeAction());

            // assert
            var navigationRequest = fakeJournal.PopEntry();
            Assert.IsTrue(fakeJournal.IsEmpty());
            Assert.AreEqual("SomeAction", navigationRequest.ActionName);
            Assert.AreEqual("TestController", navigationRequest.ControllerName);
            Assert.AreEqual(navigator.NavigationFrameName, navigationRequest.NavigationFrameName);
        }

        [TestMethod]
        public void navigate_to_using_complete_expression_with_single_argument()
        {
            // arrange
            var viewModel = new ViewModelBase { Navigator = navigator };
            var requestBuilder = new ControllerActions<FakeController>(viewModel);

            // act
            requestBuilder.Invoke(c => c.SomeAction(1));

            // assert
            var navigationRequest = fakeJournal.PopEntry();
            Assert.IsTrue(fakeJournal.IsEmpty());
            Assert.AreEqual("SomeAction", navigationRequest.ActionName);
            Assert.AreEqual("TestController", navigationRequest.ControllerName);
            Assert.AreEqual(navigator.NavigationFrameName, navigationRequest.NavigationFrameName);
            //TODO I recon it should be pretty easy to get this to pass
            //Assert.Equal("arg", navigationRequest.Parameters.Single().Name);
            var actual = navigationRequest.Parameters.Single().Value;
            Assert.AreEqual(1, actual);
        }

        [TestMethod]
        public void navigate_to_using_complete_expression_with_single_property_argument()
        {
            // arrange
            var viewModel = new ViewModelBase { Navigator = navigator };
            var requestBuilder = new ControllerActions<FakeController>(viewModel);

            // act
            requestBuilder.Invoke(c => c.SomeAction(One));

            // assert
            var navigationRequest = fakeJournal.PopEntry();
            Assert.IsTrue(fakeJournal.IsEmpty());
            Assert.AreEqual("SomeAction", navigationRequest.ActionName);
            Assert.AreEqual("TestController", navigationRequest.ControllerName);
            Assert.AreEqual(navigator.NavigationFrameName, navigationRequest.NavigationFrameName);
            //TODO I recon it should be pretty easy to get this to pass
            //Assert.Equal("arg", navigationRequest.Parameters.Single().Name);
            Assert.AreEqual(1, navigationRequest.Parameters.Single().Value);
        }

        [TestMethod]
        public void calculates_overload_syntax_correctly()
        {
            // arrange
            var viewModel = new ViewModelBase { Navigator = navigator };
            var requestBuilder = new ControllerActions<FakeController>(viewModel);

            // act
            var localList = new List<string>();
            try
            {
                requestBuilder.Invoke(o => o.SomeAction(One, localList));
            }
            catch (NotSupportedException ex)
            {
                Assert.IsTrue(ex.Message.EndsWith("Controller<TestController>().Invoke(o => o.SomeAction, One, localList)"));
            }
        }

        public int One
        {
            get { return 1; }
        }
    }
}